package com.sunyard.authorization.filter;

import com.auth0.jwt.exceptions.TokenExpiredException;
import com.sunyard.authorization.config.CustomizedToken;
import com.sunyard.constant.manage.CommonConst;
import com.sunyard.dal.mapper.PopedomMapper;
import com.sunyard.redisUtil.RedisUtil;
import com.sunyard.utils.TokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

@Slf4j
public class JWTFilter extends BasicHttpAuthenticationFilter {

    /**
     * 判断是否允许通过
     *
     * @param request
     * @param response
     * @param mappedValue
     * @return
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        log.info("isAccessAllowed方法");
        try {
            if (request instanceof HttpServletRequest) {
                if (((HttpServletRequest) request).getMethod().toUpperCase().equals("OPTIONS")) {
                    return true;
                }
            }
            return executeLogin(request, response);
        } catch (Exception e) {
            log.info("错误:" + e);
            // responseError(response,"shiro fail");
            return false;
        }
    }

    /**
     * 是否进行登录请求
     * @param request
     * @param response
     * @return
     */
    /*@Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        log.info("isLoginAttempt方法");
        HttpServletRequest request1=(HttpServletRequest)request;
        String token=request1.getHeader("token");
        log.info("token="+token);
        if (token!=null){
            return true;
        }
        return false;
    }*/

    /**
     * 创建shiro token
     *
     * @param request
     * @param response
     * @return
     */
    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        log.info("createToken方法");
        String jwtToken = this.getAuthzHeader(request);
        log.info("创建token时，token内容：", jwtToken);
        if (jwtToken != null) {
            return new CustomizedToken(jwtToken);
        }
        return new CustomizedToken(jwtToken);
    }

    /**
     * isAccessAllowed为false时调用，验证失败
     *
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        log.info("onAccessDenied");
        this.sendChallenge(request, response);
        return false;
    }


    /**
     * shiro验证成功调用
     *
     * @param token
     * @param subject
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
        log.info("onLoginSuccess：");
        ServletContext servletContext = request.getServletContext();
        ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
        PopedomMapper popedomMapper = (PopedomMapper) context.getBean("popedomMapper");
        //List<String> list =popedomMapper.getAllpopedomLink();
        String key = "popedom:all";
        List<String> list = (List<String>) RedisUtil.get(key);
        if (null == list) {
            list = popedomMapper.getAllpopedomLink();
            RedisUtil.set(key, list);
        }
        String uri = ((HttpServletRequest) request).getRequestURI();
        int indexBegin = uri.indexOf("/", 2);
        log.info(uri.substring(indexBegin));
        //TODO 权限表需完善
        if (list.contains(uri.substring(indexBegin))) {
            // String popedomId = popedomMapper.getPopedomIdByuri(uri.substring(indexBegin));
            List<String> popedomIdList = popedomMapper.getPopedomIdByuri(uri.substring(indexBegin));
            for (String popedomId: popedomIdList) {
                JdbcRealm jdbcRealm = new JdbcRealm();
                jdbcRealm.setPermissionsLookupEnabled(true);
                try {
                    subject.checkPermission(popedomId);
                } catch (Exception e) {
                    e.printStackTrace();
                    log.info("当前用户没有该操作权限");
                    HttpServletResponse httpResponse = (HttpServletResponse) response;
                    httpResponse.setCharacterEncoding("UTF-8");
                    httpResponse.setContentType("application/json; charset=utf-8");
                    httpResponse.getWriter().print(CommonConst.getResult(CommonConst.NO_POPEDOM, "当前用户没有该操作权限"));
                    return false;
                }
            }

        }
        return true;
    }


    /**
     * 拦截器的前置方法，此处进行跨域处理
     *
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        log.info("请求路径{}", String.valueOf(httpServletRequest.getRequestURL()));
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            log.info("OPTIONS请求不做拦截操作");
            return true;
        }
        HttpServletRequest request1 = (HttpServletRequest) request;
        String token = request1.getHeader("token");
        if (token != null) {
            String opName = "login:operator:" + TokenUtil.getOpName(token);
            Long currentTime = TokenUtil.getCurrentTime(token);
            if (RedisUtil.hasKey(opName)) {
                Long currentTimeMillisRedis = (Long) RedisUtil.get(opName);
                if (!currentTimeMillisRedis.equals(currentTime)) {
                    log.info("账号已在其它地方登录");
                    HttpServletResponse httpResponse = (HttpServletResponse) response;
                    httpResponse.setCharacterEncoding("UTF-8");
                    httpResponse.setContentType("application/json; charset=utf-8");
                    httpResponse.getWriter().print(CommonConst.getResult(CommonConst.OP_OCCUPIED, "账号已在其它地方登录"));
                    return false;
                }
            }
            try {
                if (TokenUtil.verify(token)) {
                    //判断Redis是否存在所对应的RefreshToken
                    if (RedisUtil.hasKey(opName)) {
                        Long currentTimeMillisRedis = (Long) RedisUtil.get(opName);
                        if (currentTimeMillisRedis.equals(currentTime)) {
                            return super.preHandle(request, response);
                        }
                    }
                }
                return true;
            } catch (Exception e) {
                if (e instanceof TokenExpiredException) {
                    log.info("TokenExpiredException token过期 准备重新颁发令牌");
                    HttpServletResponse httpResponse = (HttpServletResponse) response;
                    httpResponse.setHeader("Authorization", token);
                    httpResponse.setHeader("Access-Control-Expose-Headers", "Authorization");
                    httpResponse.setCharacterEncoding("UTF-8");
                    httpResponse.setContentType("application/json; charset=utf-8");
                    httpResponse.getWriter().print(CommonConst.getResult(CommonConst.PAST_DUE, "token已过期"));
                    return false;
                }
            }

        }
        return super.preHandle(request, response);

    }

}
